package gui.batches;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.Timer;

import core.Amount;
import core.Barcode;
import core.ContainerType;
import core.CoreFacade;
import core.Item;
import core.Product;
import core.ProductContainer;
import core.StorageUnit;
import core.Unit;

import gui.common.*;
import gui.inventory.*;
import gui.item.ItemData;
import gui.product.*;

/**
 * Controller class for the add item batch view.
 */
public class AddItemBatchController extends Controller implements
		IAddItemBatchController 
{

	/** Holds the latest value for the useScanner variable */
	private boolean currentUseScanner = false;
	
	/** Holds a pointer to the model */
	private CoreFacade model = null;
	
	/** Holds a list of added items to the system */
	private Map<Product, List<Item>> addedItems = null;
	
	/** Holds a list of added Products during this event */
	private List<Product> addedProducts = null;
	
	/** Holds the Product Container that we are adding these items to */
	private ProductContainer pcTarget = null;
	
	private boolean productAdded;
	
	
	private Timer scannerTimer = new Timer(500,
			
			new ActionListener()
			{

				@Override
				public void actionPerformed(ActionEvent arg0) 
				{
					addItem();	
				}
		
			}
			
			);
	
	/**
	 * Constructor.
	 * 
	 * @param view Reference to the add item batch view.
	 * @param target Reference to the storage unit to which items are being added.
	 */
	public AddItemBatchController(IView view, ProductContainerData target) 
	{
		super(view);
		
		//assign the view
		//this.view = ((AddItemBatchView)view);
		//assign the model
		this.model = CoreFacade.getInstance();
		model.clearHistory();
		//setupt the Items list
		addedItems = new HashMap<Product, List<Item>>();
		addedProducts = new ArrayList<Product>();
		productAdded = false;
		
		//addedItemData = new HashMap<ProductData, ArrayList<ItemData>>();
		//addedProductData = new ArrayList<ProductData>();
		
		//get the productContainer we are adding this too
		pcTarget = (ProductContainer) target.getTag();
		construct();
	}

	/**
	 * Returns a reference to the view for this controller.
	 */
	@Override
	protected IAddItemBatchView getView() 
	{
		return (IAddItemBatchView) super.getView();
	}

	/**
	 * Loads data into the controller's view.
	 * 
	 *  {@pre None}
	 *  
	 *  {@post The controller has loaded data into its view}
	 */
	@Override
	protected void loadValues() 
	{	
		addedProducts = model.getHistoryProductList();
		addedItems = model.getHistoryMap();
		//if (addedProducts.size() == 0) return;
		
		
		Product selected = null;
		ProductData newSelected = null;
		
		if (getView().getSelectedProduct() != null)
		{
			selected = (Product)getView().getSelectedProduct().getTag();
		}
		
		ProductData[] addedProductsData = new ProductData[addedProducts.size()];
		
		//load the products
		for (int i = 0; i < addedProducts.size(); i++)
		{
			//load all the products
			addedProductsData[i] = convertToProductData(addedProducts.get(i), 
													addedItems.get(addedProducts.get(i)).size());
			
			if ((selected != null) && 
			(addedProducts.get(i).getDescription().compareTo(selected.getDescription()) == 0))
			{
				newSelected = addedProductsData[i];
			}
		}
		
		//set the product Display
		getView().setProducts(addedProductsData);
		
		getView().selectProduct(newSelected);
	}
	
	protected void getValues()
	{
		
	}
	

	/**
	 * Sets the enable/disable state of all components in the controller's view.
	 * A component should be enabled only if the user is currently
	 * allowed to interact with that component.
	 * 
	 * {@pre None}
	 * 
	 * {@post The enable/disable state of all components in the controller's view
	 * have been set appropriately.}
	 */
	@Override
	protected void enableComponents() 
	{
		getView().enableUndo(model.canUndo());
		getView().enableRedo(model.canRedo());
		getView().enableItemAction(false);
		
		getView().setUseScanner(true);
		currentUseScanner = true;
		
		getView().setCount("1");
	}
	
	/**
	 * Used to enable/disable components with the associated view
	 * 
	 */
	public void enableDisableComponents()
	{
		getView().enableUndo(model.canUndo());
		getView().enableRedo(model.canRedo());
		checkAddItemEnable();
	}
	
	/**
	 * This method is called when the "Entry Date" field in the
	 * add item batch view is changed by the user.
	 */
	@Override
	public void entryDateChanged() 
	{
		enableDisableComponents();
	}

	/**
	 * This method is called when the "Count" field in the
	 * add item batch view is changed by the user.
	 */
	@Override
	public void countChanged() 
	{
		enableDisableComponents();
	}

	/**
	 * This method is called when the "Product Barcode" field in the
	 * add item batch view is changed by the user.
	 */
	@Override
	public void barcodeChanged() 
	{
		if (getView().getUseScanner())
		{
			//do timer thing
			if (scannerTimer.isRunning())
			{
				scannerTimer.restart();
			}
			else if (!scannerTimer.isRunning())
			{
				scannerTimer.start();
			}
			
			if (getView().getBarcode().length() == 0) 
			{
				scannerTimer.stop();
			}
		}
		else
		{
			enableDisableComponents();
		}
	}
	
	/**
	 * This method is called when any of the fields in the
	 * add item batch view have been changed by the user.
	 */
	@Override
	public void valuesChanged()
	{
	}

	/**
	 * This method is called when the "Use Barcode Scanner" setting in the
	 * add item batch view is changed by the user.
	 */
	@Override
	public void useScannerChanged() 
	{
		if (getView().getUseScanner() != currentUseScanner)
		{
			getView().setBarcode("");
			getView().enableItemAction(false);
			currentUseScanner = getView().getUseScanner();
		}
	}

	/**
	 * This method is called when the selected product changes
	 * in the add item batch view.
	 */
	@Override
	public void selectedProductChanged() 
	{
		System.out.println("Selected Product Changed");
		
		reloadProducts();
	}

	/**
	 * This method is called when the user clicks the "Add Item" button
	 * in the add item batch view.
	 */
	@Override
	public void addItem() 
	{
		int numItemsToAdd;
		
		//ProductData selectedProduct = getView().getSelectedProduct();
		//ItemData selectedItem = getView().getSelectedItem();
		
		try
		{
			numItemsToAdd = Integer.parseInt(getView().getCount());
		}
		catch (Exception e)
		{
			numItemsToAdd = -1;
		}
		
		if (numItemsToAdd <= 0)
		{
			resetData();
			scannerTimer.stop();
			getView().displayErrorMessage("Invalid Count.");
			return;
		}
		
		Product p = model.getProduct(getView().getBarcode());
		
		if (p == null)
		{
			displayAddProductView();
			//model.setProductAddFlag(true);
			productAdded = true;
			//NEW PRODUCT WAS ADDED
			p = model.getProduct(getView().getBarcode());
			
			if (p == null)
			{
				//cancel was pushed
				System.out.println("Cancel Was pushed");
				resetData();
				scannerTimer.stop();
				return;
			}
			else
			{
				Date createdDate = getView().getEntryDate();
				Calendar date = Calendar.getInstance();
				date.setTime(createdDate);
				p.setCreationDate(date);
				//model.addProduct(p, /*New Product Container*/);
				/*try 
				{
					model.addProduct(p, pcTarget);
				} 
				catch (Exception e) 
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}*/
				//System.out.println("Set the date");
			}
			
		}
		else
			productAdded = false;
			//model.setProductAddFlag(false);
		//System.out.println("Still Adding Product");
		
		
		/*ArrayList<Item> toAdd;
		if (addedItems.containsKey(p))
		{
			toAdd = addedItems.get(p);
		}
		else
		{
			toAdd = new ArrayList<Item>();
			//set the map data
			addedItems.put(p, toAdd);
			addedProducts.add(p);
		}*/
		//
		for (int i = 0; i < numItemsToAdd; i++)
		{
			if(i > 0)
			{
				//model.setProductAddFlag(false);
				productAdded = false;
			}
			Item newItem;// = new Item(p, new StorageUnit("unit 1"), new GregorianCalendar());
			//System.out.println("For loop to add");
			
			try {
				/*newItem = model.addItem(p.getBarcodeString(), new GregorianCalendar(), 
										1, (StorageUnit)pcTarget);*/
				Date viewDate = getView().getEntryDate();
				Calendar entry = new GregorianCalendar();
				entry.setTime(viewDate);
				newItem = model.addItem(p.getBarcodeString(), 
						                entry, 
						                1, (StorageUnit)pcTarget, i, productAdded);
				if(newItem == null)
					getView().displayErrorMessage("Error Adding Items");
				//toAdd.add(newItem);
			} 
			catch (Exception e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
				
		}
		enableDisableComponents();
		enableComponents();
		resetData();
		
		//load the data that was just added into the view
		loadValues();
		reloadProducts();
		
		//select the previously selected stuff
		//getView().selectProduct(selectedProduct);
		//getView().selectItem(selectedItem);
		
		//delete the scanner timer
		scannerTimer.stop();
		
	}
	
	/**
	 * This method is called when the user clicks the "Redo" button
	 * in the add item batch view.
	 */
	@Override
	public void redo() 
	{
		model.redo();
		//locationInCommands++;
		enableComponents();
		resetData();
		loadValues();
		reloadProducts();
	}

	/**
	 * This method is called when the user clicks the "Undo" button
	 * in the add item batch view.
	 */
	@Override
	public void undo() 
	{
		model.undo();
		//remove items from list
		/*BatchCommand command = commands.get(locationInCommands-1);
		locationInCommands--;
		ArrayList<Item> items = (ArrayList<Item>) addedItems.get(command.prod);
		int indexToStop = items.size()-command.itemCount-1;
		for(int i = items.size()-1; i > indexToStop; i--)
		{
			items.remove(i);
		}*/
		//reset the barcode stuff
		
		enableComponents();
		resetData();
		loadValues();
		reloadProducts();
	}

	/**
	 * This method is called when the user clicks the "Done" button
	 * in the add item batch view.
	 */
	@Override
	public void done() 
	{
		addedProducts = model.getHistoryProductList();
		addedItems = model.getHistoryMap();
		ArrayList<Item> data = new ArrayList<Item>();
		
		for (int i = 0; i < addedProducts.size(); i++)
		{
			data.addAll(addedItems.get(addedProducts.get(i)));
		}
		
		if (data.size() > 0)
		{
			String file = model.generateBarcodes(data);
			
			if (file.compareTo("") == 0)
			{
				//error
				System.out.println("Error Printing Barcodes");
			}
			else
			{
				try 
				{
					java.awt.Desktop.getDesktop().open(new File(file));
				} 
				catch (IOException e) 
				{
					// TODO Auto-generated catch block
					System.out.println("Error Displaying Barcodes");
				}
			}
		}
		model.clearHistory();
		getView().close();
	}
	
	/**
	 * This signals the view to display the addProductView
	 */
	public void displayAddProductView()
	{
		getView().displayAddProductView();
	}


	//*********************************************************
	//* Private methods for this class
	//*********************************************************
	private void checkAddItemEnable()
	{
		int number;
		try
		{
			number = Integer.parseInt(getView().getCount());
		}
		catch (Exception e)
		{
			number = -1;
		}
		
		//check if count is valid
		if (number <= 0)
		{
			getView().enableItemAction(false);
			return;
		}

		//Check if valid barcode
		if (getView().getBarcode().length() == 0)
		{
			getView().enableItemAction(false);
			return;
		}
		
		if (model.canAddItem(getView().getBarcode()) == false)
		{
			getView().enableItemAction(false);
			return;
		}
		//-----------------------------------------------
		
		//check if it is a Valid date
		if (getView().getEntryDate() == null)
		{
			getView().enableItemAction(false);
			return;
		}
		
		if (model.isValidDate(getView().getEntryDate()) == false)
		{
			getView().enableItemAction(false);
			return;
		}
		//-----------------------------------------------
		
		getView().enableItemAction(true);
	}
	
	/**
	 * This method will convert a Product to the internal data unit of
	 * ProductData type. 
	 * 
	 * @param p
	 * @return
	 */
	/*private ProductData convertToProductData(Product p)
	{
		ProductData newData = new ProductData();
		
		newData.setBarcode(p.getBarcodeString());
		newData.setCount("" + addedItems.get(p).size());
		newData.setDescription(p.getDescription());
		newData.setShelfLife(String.valueOf( p.getShelfLife() ) );
		newData.setSize(p.getAmount().toString());
		newData.setSupply(String.valueOf( p.getThreeMonthSupply() ) );
		newData.setTag(p);
		
		return newData;
	}
	
	private ItemData convertToItemData(Item i)
	{
		ItemData newData = new ItemData();

		newData.setBarcode(i.getBarcodeString());
		newData.setEntryDate(i.getEntryDate().getTime());
		
		if (i.getExpirationDate() != null)
		{
			newData.setExpirationDate(i.getExpirationDate().getTime());
		}
		else
		{
			newData.setExpirationDate(null);
		}
		
		
		
		
		//newData.setProductGroup(i.getProductContainer().getName());
		if (i.getProductContainer().getContainerType() == ContainerType.STORAGE_UNIT)
		{
			newData.setStorageUnit(i.getProductContainer().getName());
		}
		else
		{
			newData.setProductGroup(i.getProductContainer().getName());
			try 
			{
				newData.setStorageUnit(
						model.getParentStorageUnit(i.getProductContainer()).getName());
			} 
			catch (Exception e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		newData.setTag(i);
		
		return newData;
	}*/
	
	/**
	 * Resets the barcode count and date
	 */
	private void resetData()
	{
		getView().setBarcode("");
		getView().setCount("1");
		
		getView().enableItemAction(false);
	}
	
	private void reloadProducts()
	{
		
		if (getView().getSelectedProduct() == null)
		{
			ItemData[] empty = new ItemData[0];
			getView().setItems(empty);
			return;
		}
		
		Item selectedItem = null;
		ItemData newSelect = null;
		
		if (getView().getSelectedItem() != null)
		{
			selectedItem = (Item)getView().getSelectedItem().getTag();
		}
		
		Product p = model.getProduct(getView().getSelectedProduct().getBarcode());
				
		ArrayList<Item> theItems = (ArrayList<Item>) addedItems.get(p);
		
		
		
		if (theItems == null) 
		{
			System.out.println("The Items are null");
			return;
		}
		
		if (theItems.size() == 0) 
		{
			System.out.println("Size == 0");
			return;
		}
		
		System.out.println("Got past Checks");
		
		ItemData[] addedItemsData = new ItemData[theItems.size()];
		
		for (int i = 0; i < theItems.size(); i++)
		{
			addedItemsData[i] = convertToItemData(theItems.get(i));
			
			if ((selectedItem != null) && 
				(selectedItem.getBarcode().equals(theItems.get(i).getBarcode()) == true))
			{
				newSelect = addedItemsData[i];
			}
		}
		
		getView().setItems(addedItemsData);
		
		getView().selectItem(newSelect);
		
	}	
}

